package digitRecognitionProblem.reduceConnections;

import genetic_algorithm.Chromosome;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

import mlp.SparseMlp;
import utils.RandomGenerator;

public class ReduceConChromosome implements Chromosome {

	// total bits in chromosome (maximal number of edges)
	public static int CHROM_LEN = SparseMlp.INPUTS * SparseMlp.HIDDEN_LAYYER
			+ SparseMlp.HIDDEN_LAYYER * SparseMlp.OUTPUT_LAYYER;
	
	private BitSet bits; // chromosome's binary data
	
	/**
	 * Constructor- creates a new chromosome with random values
	 */
	public ReduceConChromosome() {
		
		// initialize bit set
		bits = new BitSet(CHROM_LEN - 1);
		
		// set a random value for each bit
		for (int i = 0 ; i < CHROM_LEN ; ++i) {
			if (RandomGenerator.nextInt(2) == 0) {
				bits.set(i);
			}
		}
		
		// correct invalid data
		validateBits(bits);
	}

	/**
	 * Constructor- creates a new chromosome with given binary data
	 * @param bits string holding chromosome's binary data
	 */
	public ReduceConChromosome(BitSet bits) {	
		
		// initialize bit set
		int numBits = bits.size();
		this.bits = new BitSet(numBits);
		
		// set the necessary bits
		for (int i = 0 ; i < numBits ; ++i) {			
			if (bits.get(i)) {
				this.bits.set(i);
			}				
		}
		
		// correct invalid data
		validateBits(this.bits);
	} 
	
	/**
	 * Checks that not all edges are turned off
	 * @param bits
	 */
	private static void validateBits(BitSet bits) {

		// validate hidden layers number- cannot be 0
		while (bits.cardinality() == 0) {
						
			// set a random value for each bit
			for (int i = 0 ; i < CHROM_LEN ; ++i) {
				if (RandomGenerator.nextInt(2) == 0) {
					bits.set(i);
				}
			}
		}
	}
	
	/**
	 * Returns index'th bit
	 */
	@Override
	public Object getValue(int index) throws IllegalArgumentException {

		// check boundaries
		if (index < 0 || index >= CHROM_LEN) {
			return null;			
		}
		
		// return bit at specified position
		return (Boolean) bits.get(index);
	}

	/**
	 * Flips index'th bit
	 * @param index index of bit to flip
	 * @throws IllegalArgumentException if index exceeds boundaries
	 */
	public void flipValue(int index) throws IllegalArgumentException {

		// check boundaries
		if (index < 0 || index >= CHROM_LEN) {
			throw new IllegalArgumentException("index exceeds data length");			
		}
		
		// flip specified bit
		bits.flip(index);
	}
	
	/**
	 * Sets index'th bit to be given value.
	 * If given value is not '0' or '1' or if index exceeds
	 * boundaries, throws IllegalArgumentException
	 */
	@Override
	public void setValue(int index, Object obj) throws IllegalArgumentException {

		// check boundaries
		if (index < 0 || index >= CHROM_LEN) {
			throw new IllegalArgumentException("index exceeds data length");			
		}

		// extract bit value from input
		Boolean bit = null;
		try {			
			bit = (Boolean) obj;
		} catch (Exception e) {
			throw new IllegalArgumentException("obj is of illegal type- received " + obj.getClass());
		}
		
		// set new value
		bits.set(index, bit);
	}

	/**
	 * Return a list where i'th element is chromosome's i'th bit
	 */
	@Override
	public List<Object> getAllValues() {
		
		List<Object> retVal = new ArrayList<Object>();
		retVal.add(bits);
		return retVal;
	}

	/**
	 * Returns all chromosome's values
	 * @return entire binary data
	 */
	public BitSet getData() {
		return bits;
	}
	
	public String toString() {
		return bits.toString();
	}
}
